Repository navigation
fix(auth): confirmSignIn identity pool errors, sign-in log redaction and the access-group signed-out record - #4405
Merged
Merged
Conversation
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 00:11 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 00:11 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 00:11 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 00:11 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 00:11 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 00:11 — with
GitHub Actions
Error
harsh62
force-pushed
the
fix/cognito-plugin-followups
branch
from
October 29, 2026 01:46
69b6234 to
1d6e23d
Compare
…rmance to remove it
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
had a problem deploying
to
IntegrationTest
October 29, 2026 23:23 — with
GitHub Actions
Error
harsh62
force-pushed
the
fix/cognito-plugin-followups
branch
from
October 29, 2026 23:24
53b33ff to
be068dc
Compare
…actor parsers' error line On the plugin's confirmSignIn the value is the user's answer, so an answer that is not a known MFA type or factor was logged at error level. Log the fixed text "Tried to initialize an unsupported MFA type" for every value instead. The log-transcript golden's scenario 7 line changes accordingly, and its pins in LogTranscriptSnapshotTests and check_fixture_lock.sh are updated. The Cognito client refuses an answer that is not a known type before the engine runs, so its line only printed values from Cognito or a saved record; two of its comments that described the old line are updated.
harsh62
force-pushed
the
fix/cognito-plugin-followups
branch
from
October 29, 2026 23:36
be068dc to
1d0b39a
Compare
thisisabhash
approved these changes
Oct 30, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Auth plugin fixes found in the review of the
AmplifyCognitoClientbeta (#4349) and its follow-ups (#4401). Each one changes what an app on the plugin sees:confirmSignInnow throws the identity pool's error when the user pool accepts the answer but the identity pool step after it fails, and signs the user out first, assignIndoes. Before, it never returned, and every Auth call queued behind it waited too.confirmSignInanswer (verbose), the TOTP setup's verified session (verbose), and the auto sign-in session of a sign-up result (info). The last one adds conformances toAmplify's publicAuthSignUpStep, recorded inapi-dump/Amplify.json.<REDACTED>in its place, and the usernames masked.confirmSignInthat value is the user's answer.{"noCredentials":{}}, in the shared keychain service instead of deleting the session record. That sign-out then stops an app extension's first access-group migration, as a login does. A guest's sign-out, and one with nothing saved, write no record.It also carries the follow-ups to nine review comments left on #4401 after approval, all in
AmplifyCognitoClientand its docs (below), the fixes for this PR's own review (below), and five follow-ups deferred from the reviews of #4401 and #4402 (below): the client keeps a session's expiry across launches, two tests for a resumed migration, CI provisioner changes, a sandbox guard and test selector changes. The CI provisioner changes need one provisioning run in the CI account after merge.Public API:
AuthSignUpStepgainsCustomStringConvertible,CustomDebugStringConvertibleandCustomReflectable, withdescription,debugDescriptionandcustomMirror. It's an additive change, recorded inapi-dump/Amplify.jsonthe same wayTOTPSetupDetails's conformances were (#4349). This PR isfix(auth), so the release that ships it needs afeatcommit too (#4403, say) to bump the minor version. An app's own description conformance forAuthSignUpStepnow gets a compiler warning and Amplify's is used; the release note tells apps to remove theirs.The parser fix changes one line of a locked golden fixture and its two pins, so this PR needs an admin merge past the "Auth | Plugin Behavior Snapshots" check (details below).
Changes
confirmSignInthrows the identity pool's error instead of never returningAWSAuthConfirmSignInTaskwaits for the sign-in to finish. Its loop had no arm for the authorization error state, so when the user pool accepted the answer andGetIdorGetCredentialsForIdentitythen failed, the call never returned, and the Auth calls queued behind it waited too. The loop now throws the error, asAWSAuthSignInTaskdoes for the same state. For example, it throwsAuthError.servicewithAWSCognitoAuthError.invalidParameterunderneath. It also logs the verbose lineAuthorization reached an error state …, whichsignInlogs there too.Before it throws, it cancels the sign-in and waits for the sign-out, as
signIn,signInWithWebUIandautoSignIndo on that failure. Otherwise the user stays signed in in memory with tokens that were never saved. The sign-in's own refresh token is revoked, not a login the credential store holds, and nothing is saved, sofetchAuthSession()reports signed out,getCurrentUser()throwssignedOut, the nextsignInstarts a new sign-in, and anotherconfirmSignInthrowsinvalidState. It cancels only when this call sent the answer. A sign-in that a shared-keychain reconcile adopted while the call waited is left as it is. A sign-out that fails ends the wait too, so the call can't hang.The cancel's sign-out no longer reads the saved session first.
InitiateSignOutprefers the user pool login the store holds over the tokens it was given, which is right for asignOut, but a sign-in cancelled from.signedIn(only ever after the identity pool step failed) never saved its tokens. So the store holds nothing or a guest, or, under a keychain access group, possibly a login another app saved during the sign-in: the cancel revoked that login and left this sign-in's refresh token valid. The engine's cancel now revokes the tokens it was given. That coverssignIn,autoSignInandsignInWithWebUItoo, which cancel the same way; with nothing saved it is the same token as before, minus the error lineUnable to update credentials with error: …the failed read logged. Under an access group the sign-out still writes the signed-out record over the other app's login, so that app is signed out at its nextfetchAuthSession; leaving the record alone, as the client does, would mean carrying the cancel through the revoke and the local sign-out to the store, so this PR documents it instead.Who sees it: apps with an identity pool that sign in with a challenge: MFA, a new password, custom auth or a factor selection. The cancel's revoke: apps with an identity pool that share a keychain access group, on every sign-in that fails at the identity pool step.
The challenge session is redacted in every printed form
The challenge a sign-in answers (the engine's
RespondToAuthChallenge) had no description of its own. So every verbose line that printed it by reflection showed its session and the username in full. These lines are the "Sending event" lines of the sign-in actions when Cognito answers with a challenge (SRP, custom auth,USER_AUTH, migrate auth, auto sign-in after sign-up, and eachconfirmSignInanswer), and the lines that print a sign-in state holding one. It now has description, debug description and mirror conformances. They print the session as<REDACTED>and mask the challenge's two username fields (aliceasal*ce), as the sign-in data already did, inprint,debugPrint, interpolation anddump.What else changes in the printed form:
parameters, Cognito's challenge parameters, still print in full, and they can hold a username (USERNAMEon a custom challenge).InternalAWSCognitoAuth.RespondToAuthChallenge(…), where default printing gaveRespondToAuthChallenge(…).Who sees it: anyone reading or collecting verbose logs, from the plugin or from
AmplifyCognitoClient, which logs the same events.Three secrets 2.63.0 logged are redacted
These were found in this PR's review. All three are on main and in 2.63.0, and they show only with the log level raised (the default is
.error). Apps that forward verbose or info logs are affected.confirmSignInanswer (verbose). While Cognito checks an answer, the sign-in's challenge state (SignInChallengeState.verifying) held it in full, and the plugin's verboseChecking next step for:line prints that state by reflection. So every answerconfirmSignInsent through a challenge was logged: an SMS, email or TOTP code, a password after aPASSWORDorPASSWORD_SRPfactor selection, a new password, a custom challenge answer. Nothing reads the answer from that state, so it no longer holds it. The answer's own printed form (ConfirmSignInEventData) is printed by the TOTP setup's verbose lines (Checking next step for:,Auth state change) and by theSending eventline of an answer sent again after Cognito refused the saved device. It masked the answer, keeping four characters of a six-digit code, and now prints<REDACTED>inprint,debugPrint, interpolation anddump.VerifyTOTPSetuplogs the event it sends, and that event carries the sessionVerifySoftwareTokenreturns, which answers the MFA setup challenge and finishes the sign-in. The event type now redacts it in every printed form: the line showsrespondToAuthChallenge("<REDACTED>"). Its other cases print as before.AmplifyCognitoClientlogs the same line.AuthSignUpStep.completeAutoSignIn(Session)had no description of its own, soconfirmSignUp, and asignUpthat Cognito confirms at once, logged the session in full. With the username, that session signs the user in.AuthSignUpStepnow printscompleteAutoSignIn("<REDACTED>")in every printed form, the step and theAuthSignUpResultholding it alike. The other steps print as default printing does, andcase .completeAutoSignIn(let session)still reads the session. The engine's copy of the step prints the same way, so the payload log gate's normalised texts still match.AmplifyCognitoClient'sAuthClientSignUpStepalready redacted it.I also looked for other states and events that hold an answer, a password or a session and that a "Sending event", "Checking next step for:" or "Auth state change" line prints. The sign-in data prints its password redacted. The sign-up state's
Auth state changeline prints its session masked, as before. The sign-up password and confirmation code ride only in events that no line prints. No golden changes: the log transcript has no challenge, TOTP setup or sign-up scenario.Who sees it: anyone reading or collecting verbose logs (the answer and the TOTP session), or info-level logs (the sign-up session), from the plugin, and from
AmplifyCognitoClientfor the TOTP session and the retried answer. Also apps that print or dump a sign-up result or step.The MFA-type and auth-factor parsers log a fixed text
EngineMFAType(rawValue:logger:),EngineAuthFactorType, and the plugin'sMFATypeandAuthFactorTypeextensions loggedTried to initialize an unsupported MFA type with value: <value>at error level. OnconfirmSignInthe value is the user's answer: an answer to an MFA selection, an MFA setup selection or a first-factor selection that is not a known type. The line is now the fixed textTried to initialize an unsupported MFA type, for every value. It keeps the same level (error) and category:MFATypeandAuthFactorTypein the plugin, andAmplifyCognitoClient.MFATypeandAmplifyCognitoClient.AuthFactorTypein the client, which logs the same line.Who sees it: anyone reading or collecting error-level logs from the plugin or the client, or matching that line's text.
Under an access group, the plugin's sign-out of a login saves the signed-out record
With a keychain access group set, every plugin path that ends a login saves
{"noCredentials":{}}in the shared service instead of deleting the session record. That coverssignOut(local or global),clearFederationToIdentityPooland the local sign-out afterdeleteUser. All of them go throughAWSCognitoAuthCredentialStore.deleteCredential(). Under an access group,deleteCredential()now reads the record first. It replaces the record with the signed-out record only when the record holds a login (user pool tokens or a federated identity). The replace is one keychain update (replaceIfPresent) that writes only over a record that's still there. So when another process in the group deletes the record after the read (an older plugin's or a guest's sign-out, or the client's purge), the sign-out still completes and writes nothing. A record that's already signed out is left as it is. Other cases keep the old behaviour:identityPoolOnly) is deleted, as is a record the store can't read.So a guest's sign-out, or a
signOut()from.signedOutwith nothing saved, never stops a login's migration. This matters when an app extension in the group signs out that way before the app's first launch with the access group: the app's migration still moves its login. Without an access group nothing changes: the record is deleted.These are the bytes
AmplifyCognitoClient's default session saves when it signs a session out (it writes nothing when there is nothing to sign out). After a plugin sign-out, an extension's first access-group migration (migrateKeychainItems: true) used to stop only at a device record. So an extension whose own keychain still held a stale login moved it in over the user who had signed out. Now the signed-out record stops that migration, as a login does. Every user-pool sign-in saves the advanced-security device ID, which already stopped it after a user-pool user's sign-out. So the record matters mainly after a federated identity's sign-out, which leaves no device record, and that's the case the tests cover. The migration rule itself is unchanged: a login or a signed-out record stops it, and a guest or a configuration record alone doesn't stop a login. The next sign-in replaces the record.How it combines with #4401's AWSMobileClient (Amplify v1) change. In #4401, a stored signed-out record stops the AWSMobileClient migration, and a guest the client fetches over a signed-out
.defaultclears those logins first..none(migrateKeychainItemsOfUserSession: true)carries the record into its unshared service, where it stops the migration too..defaultfetches over the record clears the AWSMobileClient logins first, best effort. Before this change, the client found no record after a plugin sign-out, and left those logins.Other effects, all under an access group:
AmplifyCognitoClient.storedSessions(…, includingSignedOut: true)in that access group now lists.defaultas a signed-out row after a plugin sign-out of a login, with no label or username. Before, it listed no.defaultrow. When the client saved a label or username beside the record, an absent record was already listed as a signed-out row with them, and it still is. The default listing is unchanged.clearFederationToIdentityPool()there, while still federated in memory, throwsAuthError.service("Unable to find credentials to clear for federation.")where it threwAuthError.unknown("Unable to clear credential store: …"), and a sign-out there no longer logs the error lineUnable to update credentials with error: ….[KeychainStore]lines:Initialized keychain…for the shared service,Started retrieving…, thenSuccessfully retrieved…orNo Keychain item found…. A read failure is logged at verbose only, and the record's contents are not logged. A sign-out of a login then logs the lines for replacing an item only if present (Replacing `Data` only if present…, thenConditional write succeeded…, orConditional write refused…when the record is gone), in place of those for removing one. A sign-out over a record that's already signed out logs neither. At the next launch the plugin reads the signed-out record instead of finding none, which changes two lines:No existing session found.is no longer logged;completedOperationwith no credentials, where it sentthrowError(itemNotFound).Compatibility with older plugins. Every plugin release reads
{"noCredentials":{}}as signed out.AmplifyCredentialshas had anoCredentialscase with synthesizedCodableback to 2.10.0 (checked at that tag), so those releases decode the record as.noCredentials, the value for no saved session. A rollback after such a sign-out starts signed out. The rollback matrix already pins the same bytes, written by the client, as read signed out by 2.62.0 and the current plugin (testMatrix_clientSignOut_readByEveryPluginAsSignedOut).Who sees it: apps with a keychain access group, especially with app extensions, and app suites whose apps share one. Also apps that list stored sessions with
includingSignedOut: true, and apps under an access group that were upgraded from AWSMobileClient.Docs
docs/design/RELEASE-NOTES-PENDING.md: a row for each change above, a Security row for each of the three leaks, and the client's rows for the redactions and the parser text. The client's parser row says that the client never logged a user's answer through the parsers: it refuses an unknown answer before the engine runs. The AWSMobileClient row added in fix(auth): AmplifyCognitoClient follow-ups from the beta review #4401 said "the plugin's own sign-out deletes the record, so an app that uses only the plugin is not affected". It now says when that holds: without an access group.Locked golden change: admin merge required
The parser fix changes one recorded log line, so the GoldenLogs tree and its two pins change together:
AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestResources/GoldenLogs/transcript.json:1005, scenario7-unsupportedAuthFactorType, categoryAuthFactorType, level error:"Tried to initialize an unsupported MFA type with value: X"→"Tried to initialize an unsupported MFA type".AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/GoldenTests/LogTranscriptSnapshotTests.swift:44:pinnedTranscriptSHA2566193c4562559fb8602ba4516c11ec533f8ee1e57cf65f851fe19afebed925afa→5daa06cfb709db3ce7b2540a9af5566bdf24de306bd20ca7246b909a3596c796. The doc comment at lines 26-29 lists the re-pin.scripts/cognito-engine/check_fixture_lock.sh:72: the GoldenLogs tree14aefeb712286ee38af02d883a50f3bcc774a610→7ef93ecd4b541aebfd5d32a78a385d62357d3065. The comments at lines 23-24 and 67-68 record the reason.No other locked tree changes: GoldenStoredFormat, GoldenErrors, GoldenKeychainQueries and GoldenConfiguration keep their pinned hashes.
Why it needs an admin merge: the lock check runs the base branch's copy of
check_fixture_lock.sh. That copy still pins the old GoldenLogs tree, and this PR also changes the script itself, which the self-file check refuses. Both are failures by design for a re-lock. The three files are admin-owned in.github/CODEOWNERS, so the change needs admin review. An admin then merges past the failing check, and the push run of the merge on main fails once. The next push runs the merged script and passes. Open PRs fail the check until they merge main.On this branch,
check_fixture_lock.sh HEADpasses (all five trees match). Run againstorigin/main, it fails in two ways. With this branch's script andorigin/mainas the base, it fails withcheck_fixture_lock.sh changed. Withorigin/main's script, it fails withGoldenLogs is locked (tree 14aefeb7…), but HEAD has 7ef93ecd…(M transcript.json) and the same self-file error.Also in this PR: review follow-ups from #4401
These are fixes for the nine comments left on #4401 after it was approved. All of them are in
AmplifyCognitoClientand its docs:fetchAuthSession()that saves a guest over.default's signed-out record runs the sign-out's AWSMobileClient clear again. So it also removes whatever AWSMobileClient, or the AWS SDK for iOS used beside the client, saved in those services since the sign-out. An app'sAWSCognitoCredentialsProviderloses the identity ID it cached for any identity pool then. The README's Sign-out section, "Ship the plugin's migrations first" and the differences table now say so, as do design §4.8, the pending release-note row and the store and guest-path comments. Behaviour is unchanged.DefaultSessionGuestLegacyLoginTests.testAGuestClearsThemBeforeItIsWrittenchecks that the guest path clears the AWSMobileClient logins before it writes the guest..resetPasswordand.confirmSignUprows say whatconfirmSignIndoes with them: it throws.invalidState, except after Cognito refused a challenge answer or a TOTP code with that step, where it sends the answer again.browserBusy(holder:)rows cover a hosted-UI sign-in already in progress, whereholderis the session itself and no sheet is held..stillClosing("Session "work"'s previous system sheet is still closing."). Before, it said the session was waiting for a sheet it held itself. This applies to the hosted-UI in-progress check and to the lock's own repeat-call refusal. A pending release-note row records it.testADoubleTapWhileAnotherSessionHoldsTheSheetDoesNotSayItIsQueuednow queues a third session first, so "queued" must mean this session is queued.AuthClientError.service's doc, and decision 37, now say "In a.partialsign-out".signOutStoredSession's Returns doc no longer names a global sign-out, which that call never sends.Also in this PR: review follow-ups from this PR
Besides the three leaks and the
confirmSignInsign-out above:replaceIfPresent, not a check-then-updateset. Outside native macOS, a delete by another process between the check and the update used to fail the sign-out after the revoke, and put the session back to.signedIn. A record that's already signed out is no longer written again.signOutanddeleteUserunder an access group are pinned outside the locked goldens.AuthError.unknowntext that no sign-in reaches. They say the sign-out clears only the signing-out app's own AWSMobileClient locations (decision 36 too), give the exception of an app that leaves its group with migration, and correct what the record changes forstoredSessionsand an extension's migration. They also list what a process that reads another process's signed-out record now sees, and quote 2.63.0's hosted-UI text. The client's parser row and two comments no longer say that the client logged an answer through the parsers.signOutStoredSession's Returns doc names the no-window hosted-UI error a sign-out that couldn't finish can carry.confirmSignInsign-out's failure exit has a test. When the credential store refuses the local clear, the sign-out that cancels the sign-in ends in an error and goes back to signed in, never to signed out. The wait ends on that error too, but no test covered it: removing it left everyconfirmSignIntest passing. A new test pins that the call returns the identity pool's error instead of hanging.confirmSignInhung only in 2.63.0 and earlier, and the row says the plugin's cancel writes the signed-out record over another app's login under an access group. Decision 40 andAccessGroupSignedOutRecordTestsno longer say a signed-out record is written over: it is left as it is. A release-note row covers the cancel's revoke and the error line it no longer logs, and the sign-up row tells apps to remove their ownAuthSignUpStepdescription conformance.Also in this PR: deferred follow-ups
Five follow-ups that the reviews of #4401 and #4402 deferred. Only the first changes what an app sees, and only in
AmplifyCognitoClient; the others are tests, CI and the integration-test scripts. None changes public API.deadTokenkey in a named session's envelope, or in.default's sidecar. Both are additive keys, written only when set, so there is no schema change. So a new client of the session, or the app after a relaunch, answers the samesessionExpiredwithout refreshing the dead token again, andsignInis allowed on it without signing out. Before, the expiry lived in memory only: the next launch refreshed the dead token once more, got the same refusal and started the 30-second rule over.infra/ci/provision-ci.sh).ccit-ci-extend-api-key, runs weekly from an EventBridge rule and extends the code-reading tests' read-only AppSync key in place, to 365 days from now, once 120 or fewer days are left. The key keeps its id, so the uploaded test configuration stays valid. Its role,ccit-ci-key-exec, may only write its own logs and list and update the code sink API's keys. It logs dates, never the key: when AppSync refuses an extension, it fails with a fixed text that carries only the error's name, never the SDK's message. The Lambda drift check covers it as the ninth CI Lambda, so a Lambda baseline can be recorded only after the post-merge--apply --scope allhas made it.--applyis refused once phase 2 exists. It stops before its dry run when any phase-2 pool, Lambda, role or the code sink's API exists, names what it found, and says to use--scope all. A plain dry run prints the same as a note and plans nothing.replace <pool>. A guarded command replaces one of the script's pools after a template change. It takes only the script's four pool names. After each tag check, it deletes the pool and what names it (its uploaded file and, for the default pool, the rotation file, the identity pool and the new-password reset Lambda), then reruns--apply --scope all --upload. It is a dry run by default. With--applyit runs both dry runs again first and deletes nothing if either refuses. It refuses whileghshows a queued or running CI run that reads the pools. Whenghis missing, or can't list the runs (not logged in, say), it only warns and goes on, so check that no run is going first.replacerefuses an uploaded file without the purpose tag.--apply --scope allruns in the CI account, the key extension doesn't exist and nothing else changes. That run makes only the new Lambda, its role, its rule and its log group, allccit-ci-additions. Only after it can the Lambda baseline be recorded again: a record needs every CI Lambda deployed, the new one included. The steps are in the team's internal runbook.lib.sh's check now also readsaws configure list. It goes on only when the profileAWS_PROFILEnames supplies the credentials itself: keys in the credentials or config file,sso,login, acredential_process, or arole_arnit assumes (assume-roleorassume-role-with-web-identity). An EC2 instance role, container credentials, a legacy credentials file, no credentials, or a profile the CLI can't resolve is refused before any AWS call. So a profile that holds only a region can no longer build the sandbox in an instance role's account. The exported-keys and web identity refusals are unchanged, and a recorded account is still checked against the caller's identity. The integration README lists what is accepted.integ_test.yml,integ_test_api.yml,integ_test_datastore.yml,unit_test.yml,build_amplify_swift_platforms.yml,build_minimum_supported_swift_platforms.yml,build_xcode_preview.yml) now runaffected_targets.py --check-bundledbefore selecting. It reads every Xcode project in the checkout and fails with an::erroron the project file for each file a project copies into a bundle that is missing from the checkout, other than the ones git ignores (the integration-test configurations CI writes before building). A project it can't read, a root with no project and a failed git query fail it too. That covers the six READMEs only stress-test targets copy (GraphQLAPIStressTests,AnalyticsStressTests,AuthStressTests,DatastoreStressTests,GeoStressTests,StorageStressTests), which no PR job builds. Documentation stays ignored by the selection wherever it lives, so editing a bundled README selects nothing (with the unit groups it no longer selects AWSPluginsCore, which the threebuild_*workflows turned into a full package build).--repo-rootdefaults to the checkout that holds the script rather than the current directory, so a run from another directory still reads this checkout's projects.ci_script_self_tests.ymlinto its ownci_selector_self_tests.yml, which also runs on a change to anyproject.pbxproj, since the check's self-test reads the real Xcode projects. A project change no longer starts the other script self-tests. The job keeps its name,Test selector; no branch protection rule or ruleset requires it.--renamesfile (git diff --name-status -M --diff-filter=R | cut -f2-, tab-separated); the changed list keeps rename detection. The selector attributes an old path like any changed file, so a file moved out of a target still selects that target's group. An old path that nothing owns, covers or ignores is skipped only when the rename's new path is itself attributed (owned by a target, or under a group'spaths); otherwise it fails closed, as its deletion does, so an unknown file moved intoscripts/or.github/selects every group rather than none. A move that changes a target's directory selects what the new paths select: movingRecordValidationTests.swiftintoAmplifyRecordCache(c6bc9e3) selects 7 unit groups, not all 22. A plain deletion still fails closed.Testing
swift build --build-testsswift test --filter AWSCognitoAuthPluginUnitTests: 1221 tests, 2 skipped, 0 failuresswift test --filter AmplifyCognitoClientTests: 1465 tests, 0 failuresswift test --filter InternalAmplifyKeychainTests: 104 tests, 0 failuresswift test --filter AmplifyTests --skip 'testPreconditionFailure|FatalError|shouldThrowFatalError': 473 tests, 10 skipped, 0 failuresscripts/cognito-engine/check_engine_deps.py --stage final,error_match_gate.sh check S9,check_engine_privacy_manifest.py, andengine_paths.txtagainst its generator: cleancheck_fixture_lock.sh HEAD: passesSwiftFormat 0.60.1 on the 69 changed Swift files: no changes. SwiftLint on the 20 changed
Amplify/andAmplifyPlugins/files: no errors.Offline, with every
AWS_*variable unset,AWS_CONFIG_FILEandAWS_SHARED_CREDENTIALS_FILEset to/dev/null, and IMDS off:python3 -m unittest discover -s scripts/python -p 'test_*.py': 17 tests, also on macOS's/usr/bin/python3(3.9);affected_targets.py --check-bundledover this checkout: 50 bundled files in 16 projects, none missing;auth_client_infra_self_tests.ymlruns them:python3 -m unittest discover -s infra -p 'test_*.py'(173 tests), everyinfra/test_*.shandinfra/ci/test_*.sh(test_provision_ci.sh: 43 checks, its rerun over the CI resources as first applied included),test_lambda_code.shalso on/usr/bin/python3(3.9), andnode --teston the Lambda tests (20);provision-ci.sh --scope allover the fake account as ci(auth): AmplifyCognitoClient CI and integration-test infrastructure follow-ups #4402's script left it plans exactly the key extension's 8 calls, allccit-ci-; the apply makes them, and a second dry run plans nothing.actionlinton the nine changed and new workflows: each reports only what it reports on main, and the newci_selector_self_tests.ymlnothing.xcodebuild build-for-testing -skipPackagePluginValidationfor the client host app's four test schemes (CognitoClientIntegrationTests,CognitoClientPluginInteropTests,CognitoClientUITests,CognitoClientWebAuthnUITests), with an emptyCOGNITO_CLIENT_INTEG_DIR: succeeded.New tests:
AWSAuthConfirmSignInTaskTests.testConfirmSignInThrowsTheIdentityPoolsError:GetIdfails after the user pool accepted the answer.confirmSignInthrows.servicewithinvalidParameter. Afterwards the machine is signed out, the refresh token was revoked once,fetchAuthSession()reports signed out,getCurrentUser()throwssignedOut, and a secondconfirmSignInthrowsinvalidStatewithout calling Cognito.AWSAuthConfirmSignInTaskTests.testConfirmSignInLeavesAnAdoptedSignInWhoseAuthorizationFailed: an adopted sign-in whose authorization failed stays signed in, and nothing is revoked.SignInAnswerRedactionTests: the answer's printed forms, the events and states that carry it (theChecking next step for:andAuth state changelines included), and the TOTP setup event's session, in every printed form; andVerifyTOTPSetup's line.SignInSecretLogTests, end to end through the plugin with a capturing logging plugin at verbose: a passwordconfirmSignIn, a TOTP setup'sconfirmSignIn, and asignUpandconfirmSignUpthat return.completeAutoSignIn. No line holds the answer (in full or masked), the TOTP code or either session in full. The result lines showcompleteAutoSignIn("<REDACTED>").AuthSignUpStepDescriptionTests(AmplifyTests): the step and the result print the session redacted, and the other steps print as default printing does.AccessGroupSignedOutRecordTests+Writes: a sign-out over a store that checks and then updates completes when the record is deleted in between. A delete after the read leaves nothing written. A signed-out record is not written again. A failed write fails the sign-out and keeps the login. The keychain queries ofsignOutanddeleteUserunder an access group are pinned.ChallengeSessionRedactionTests: the challenge, the events and states that carry it, and the SRP verify-password line print without the session, in every printed form.EngineStaticLogSiteTests,EngineStaticSiteRoutingTestsand the two fork tests: the parsers' error line no longer holds the answer on the plugin's and the engine'sconfirmSignInpaths.ChallengeSessionRedactionTests.testAnUnresolvableMFASetupErrorHoldsTheChallengeWithoutItsSession: theSignInError.unknownof an SMS-only MFA setup, and theAuthErrorthe plugin throws with it, hold the challenge with the session<REDACTED>and no username in full. No sign-in reaches that throw today, so the test runs the action on its own.AccessGroupSignedOutRecordTests(and its+Migrationsfile): each path that ends a login saves the record under an access group and deletes it without one. The tests also cover:signOut()with nothing saved, and a guest'ssignOut(): neither writes a record, and the app's first migration then moves its login;.defaultreading the record;AWSAuthConfirmSignInTaskTests.testConfirmSignInCancelRevokesTheSignInsOwnTokensNotTheSavedLogin: with a credential store that holds another user pool login, the cancel after a failedGetIdrevokes onlyrefreshTokenXXX, the sign-in's own, and the machine is signed out.AWSAuthConfirmSignInTaskTests.testConfirmSignInThrowsTheIdentityPoolsErrorWhenTheSignOutCannotClearTheStore: with a credential store that refuses the local clear,confirmSignInstill throws the identity pool's error within the timeout, after one revoke and one refused delete, and the state machine goes back to signed in.SavedExpiryTests: a released client, a relaunch over the same keychain for a named session and for.default, a refused token, another process's core, a sign-in, another process's new login, the plugin's new login beside a stale sidecar marker, a new federation, a failed save, and the stored format. AlsoWebUISignOutTests.testClosingTheLogoutPageOfASessionExpiredBeforeARelaunchStillSignsOut. The reuse-reset test of the refused-token branch is nowCredentialProviderTests.testARefusalAsInvalidEndsTheReuseCount: the text comes from the saved cause, so the reset only ends the count of reuses in a row, which shows when the expiry could not be saved.AWSCognitoAuthCredentialStoreInterruptedMigrationTests.testAResumedMigrationKilledBeforeTheLoginMovedStillCarriesItAfterAnExtensionStartsAgainandGuestFirstAccessGroupMigrationTests.testAResumedMigrationDoesNotCarryTheLoginOverALoginAnExtensionSavedSince.test_provision_ci.sh: the key extension's function, role and rule; the key at, within and past the reuse threshold; an expired key extended in place, or replaced when AppSync refuses; the phase-1 refusal; the fake's SNS caller-role trust check and the remade SMS pool, without git history; andreplace(its names, its dry run and CI-run refusal, its tag checks, the default pool, and phase 1 alone).test_extend_api_key.mjsfor the Lambda itself, including that a refused extension of a live key fails with a fixed text that doesn't name the key.test_provision.sh: a first run goes on with the profile's own credentials, refuses an instance role or another fallback, and a recorded account is still checked against the caller's identity.test_affected_targets.py: the rename rule, the default repo root, a bundled README renamed away from.md, and the bundled-file check, including a missing file whose name holds%and a newline, named on one escaped::errorline.DefaultSessionGuestLegacyLoginTests.testAGuestClearsThemBeforeItIsWritten: the guest path clears the AWSMobileClient logins while.defaultstill holds the signed-out record, before it writes the guest.WebUISignInTests.testADoubleTapQueuedBehindItsOwnClosingSheetSaysItIsStillClosing: a double tap after a cancel, with a.wait(timeout:)retry queued behind the session's own closing sheet, gets.stillClosingwith each policy. The retry then signs in.SystemSheetLockTests.testARepeatCallQueuedBehindItsOwnClosingSheetIsToldItIsStillClosing: the lock's own repeat-call refusal, in the same case.WebUISignInInProgressTests.testADoubleTapWhileAnotherSessionHoldsTheSheetDoesNotSayItIsQueuednow queues a third session before the double tap.Each fix's tests were checked to fail with only that fix reverted, in a scratch copy:
anotherAppsRefreshToken)..signingOut(.error)exit removed fromconfirmSignIn's wait for the cancel: only the new failed-clear test fails (it never returns). The other 27confirmSignIntests pass.::errormessage left unescaped: only the new%-and-newline test fails.confirmSignInarm: the new test fails (it never returns).testConfirmSignInThrowsTheIdentityPoolsErrorfails. Cancelling an adopted sign-in too:testConfirmSignInLeavesAnAdoptedSignInWhoseAuthorizationFailedfails..verifying:SignInSecretLogTests.testConfirmSignInLogsNoLineWithTheAnswerand the state check inSignInAnswerRedactionTestsfail. The answer masked again in its own printed form: the two answer tests inSignInAnswerRedactionTestsand the TOTP setup's end-to-end test fail.SignInAnswerRedactionTestsand the end-to-end TOTP test fail.AuthSignUpStepprinting its session, with the conformances kept: two of the threeAuthSignUpStepDescriptionTests(the third checks the other steps), the sign-up end-to-end test andEnginePayloadLogTextTests' sign-up test fail. The engine's copy printing it alone: onlyEnginePayloadLogTextTests' sign-up test fails.setagain: the check-then-update race test, the delete-after-read test and the query test fail. A signed-out record written again: only its no-write test fails. Atry?on the write: only the failed-write test fails..stillClosingused again for a queued call: in the lock, only theSystemSheetLockTeststest fails; in the hosted-UI in-progress check, only theWebUISignInTeststest fails.ChallengeSessionRedactionTestsfail, the error-text test included. The fifth checks a challenge with no session.AccessGroupSignedOutRecordTestsfail. The 3 that pass are the no-access-group case, and the guest and nothing-saved sign-outs, which delete or write nothing either way.LogTranscriptSnapshotTests,EngineStaticLogSiteTests,EngineStaticSiteRoutingTestsand the two fork tests..alreadyQueuedanswered again when the session holds the sheet: in the in-progress check, only the newWebUISignInTeststest fails; in the lock, only the newSystemSheetLockTeststest fails.holderAndQueue(for:)reporting any waiter as this session's (!waiters.isEmpty): only the updated in-progress test fails.Follow-ups